package com.luci.security.support;

import com.alibaba.fastjson.JSON;
import com.luci.config.AuthProperties;
import com.luci.security.CustomConfigAttribute;
import com.luci.security.result.AuthResultAdapter;
import com.luci.security.result.JwtUser;
import com.luci.util.HttpServletResponseUtils;
import com.luci.util.JwtUtils;
import io.jsonwebtoken.ExpiredJwtException;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.FilterInvocation;

/**
 * 默认权限处理实现
 *
 * @author yuyanan
 * @version 1.0
 * @date 18-11-14
 */
@Slf4j
public abstract class AbstractAuthAdapter implements IAuth {

    @Autowired
    protected AuthProperties authProperties;

    @Autowired
    protected AuthResultAdapter authResultAdapter;

    /**
     * 认证后可以做一些事情
     */
    protected void afterDecide(Authentication authentication, FilterInvocation filterInvocation){

    }

    /**
     * 认证前可以做一些事情
     */
    protected void beforeDecide(Authentication authentication, FilterInvocation filterInvocation){

    }

    /**
     * 获取当前url的所需要的权限 如果返回为null 或者empty list 表示该url不需要授权
     * @param url 请求的url
     * @param request  HttpServletRequest
     * @param response HttpServletResponse
     * @return List<String>  允许的角色集合
     */
    protected abstract List<String> getRolesByRequestUrl(String url, HttpServletRequest request, HttpServletResponse response );

    @Override
    public Authentication basic(HttpServletRequest request, HttpServletResponse response) {
        String authorization = request.getHeader(authProperties.getJwtHeaderKey());

        if (authorization == null || !authorization.startsWith(authProperties.getJwtHeaderValueStart())) {
            log.error("Authorization is error!");
            return null;
        }

        try {
            String jwt = authorization.replace(authProperties.getJwtHeaderValueStart(), "").trim();
            log.debug("client jwt:{}", jwt);
            Map<String, Object> map = (Map<String, Object>) JwtUtils.parseJwtToBean(jwt, authProperties.getSecret());
            JwtUser jwtUser = new JwtUser();
            jwtUser.setRoles((List<String>) map.get("roles"));
            jwtUser.setDetails(map.get("details"));
            List<SimpleGrantedAuthority> roles = jwtUser.getRoles().stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());

            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(null, null, roles);
            authentication.setDetails(JSON.toJSONString(jwtUser));
            return authentication;
        } catch (ExpiredJwtException e) {
            log.error("token 超时失效!", e);
            throw new AccessDeniedException("token 超时失效!");
        } catch (Exception e) {
            log.error("token 解析错误!", e);
            return null;
        }

    }

    @Override
    public Collection<ConfigAttribute> getRolesByUrl(FilterInvocation filterInvocation) {
        List<String> roles = getRolesByRequestUrl(filterInvocation.getRequestUrl(), filterInvocation.getRequest(), filterInvocation.getResponse());
        if (roles == null || roles.isEmpty()) {
            return null;
        }
        return roles.stream().map(CustomConfigAttribute::new).collect(Collectors.toList());
    }

    @Override
    public void decide(Authentication authentication, Collection<ConfigAttribute> configAttributes,
                       FilterInvocation filterInvocation) {

        beforeDecide(authentication, filterInvocation);

        //如果该url没有对应的role  则表示该url不需要鉴定权限 直接放行
        if (configAttributes == null) {
            log.debug(filterInvocation.getRequestUrl() + "is pass");
            return;
        }

        //如果用户拥有的角色与该url所需要的角色有并集 则通过
        configAttributes.retainAll(authentication.getAuthorities());
        if (configAttributes.isEmpty()) {
            //没有权限
            throw new AccessDeniedException("have not Auth！");
        }

        afterDecide(authentication, filterInvocation);
    }

    @Override
    public void accessDenied(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
        throws IOException {
        log.error("access denied!", e);
        HttpServletResponseUtils.responseWrite(response, authResultAdapter.acessDenied());
    }

    @Override
    public void anonymousDenied(HttpServletRequest request, HttpServletResponse response,
                                AuthenticationException authException) throws IOException {
        log.warn("have not login!", authException);
        HttpServletResponseUtils.responseWrite(response, authResultAdapter.noToken());
    }

}
